   #!coding:utf-8
# vim: tabstop=4 shiftwidth=4 softtabstop=4
"""
    @author jay.han
"""
import logging
import random
import socket
import tempfile
import shutil
import contextlib
import os
from cvirt.cnode.connection import get_connection
from xml.etree import ElementTree
import time
from cvirt.cnode.vm import power_state
from cvirt.common import exception,utils,config
from eventlet import tpool

conf = config.load_settings()

LOG = logging.getLogger(__name__)


def get_hostname():
    return socket.gethostname()

def execute(*cmd, **kwargs):
    return utils.execute(*cmd,**kwargs)

def chown(path, owner):
    execute('chown', owner, path)
    
def create_image(disk_format, path, size):
    execute('qemu-img', 'create', '-f', disk_format, path, size)

def create_cow_image(backing_file, path):
    execute('qemu-img', 'create', '-f', 'qcow2', '-o',
             'cluster_size=2M,backing_file=%s' % backing_file, path)

def get_disk_size(path):
    out, _ = execute('qemu-img', 'info', path)
    size = [i.split('(')[1].split()[0] for i in out.split('\n')
        if i.strip().find('virtual size') >= 0]
    return int(size[0])

def copy_file(src, dest):
    shutil.copy(src, dest)

def resize_disk(disk_path,resize_size):
    execute('qemu-img','resize',disk_path,'+'+str(resize_size)+'G')

def create_snapshot(disk_path, snapshot_name):
    execute('qemu-img','snapshot','-c',snapshot_name,disk_path)

def restore_snapshot(disk_path,snapshot_name):
    execute('qemu-img','snapshot','-a',snapshot_name,disk_path)
    
def delete_snapshot(disk_path, snapshot_name):
    execute('qemu-img','snapshot','-d',snapshot_name,disk_path) 

def extract_snapshot(disk_path, source_fmt, snapshot_name, out_path, dest_fmt):
    """Extract a named snapshot from a disk images    """
    execute('qemu-img','convert','-f',source_fmt, '-O',dest_fmt,'-s',snapshot_name,disk_path,out_path)
    
def create_local(target, size, unit='G'):
    execute('qemu-img', 'create', '-f', 'raw', target, '%d%c' % (size, unit))
    
def ensure_fold(base_path):
    if "," in base_path:
        path = base_path.split(",")
        for p in path:
            if not os.path.exists(p):
                os.makedirs(p)
    else:
        if not os.path.exists(base_path):
                os.makedirs(base_path)


"""@contextmanager decorator.

    Typical usage:

        @contextmanager
        def some_generator(<arguments>):
            <setup>
            try:
                yield <value>
            finally:
                <cleanup>

    This makes this:

        with some_generator(<arguments>) as <variable>:
            <body>

    equivalent to this:

        <setup>
        try:
            <variable> = <value>
            <body>
        finally:
            <cleanup>

    """    
@contextlib.contextmanager
def tempdir(**kwargs):
    tmpdir = tempfile.mkdtemp(**kwargs)
    try:
        yield tmpdir
    finally:
        try:
            shutil.rmtree(tmpdir)
        except OSError, e:
            LOG.debug('Could not remove tmpdir: %s' % str(e))
            
"""
    os.umask(mask)

    Set the current numeric umask and return the previous umask.
    umask 是指 007 770 这种权限
"""
def write_to_file(path, contents,umask=None):
    if umask:
        os_xxx = os.umask(umask)
    try:
        with open(path, 'w') as f:
            f.write(contents)
    finally:
        if umask:
            os.umask(os_xxx)
            
def get_fs_info(path):
    """Get free/used/total space info for a filesystem

    :param path: Any dirent on the filesystem
    :returns: A dict containing:

             :free: How much space is free (in bytes)
             :used: How much space is used (in bytes)
             :total: How big the filesystem is (in bytes)
    """
    total =0 
    free =0
    used = 0 
    if ',' in path:
        path_list =  path.split(',')
        for _path in path_list:
            hddinfo = os.statvfs(_path)
            total += hddinfo.f_frsize * hddinfo.f_blocks
            free += hddinfo.f_frsize * hddinfo.f_bavail
            used += hddinfo.f_frsize * (hddinfo.f_blocks - hddinfo.f_bfree)
    else:
        hddinfo = os.statvfs(path)
        total = hddinfo.f_frsize * hddinfo.f_blocks
        free = hddinfo.f_frsize * hddinfo.f_bavail
        used = hddinfo.f_frsize * (hddinfo.f_blocks - hddinfo.f_bfree)
    return {'total': total,
            'free': free,
            'used': used}

def clean(target):
    if os.path.exists(target):
        shutil.rmtree(target)

def copy_image(src, dest, host=None):
    """Copy a disk image to an existing directory
    """
    if not host:
        if not os.path.exists(dest):
            execute('mkdir','-p',dest)
        execute('cp', src, dest)
    else:
        dest = "%s:%s" % (host, dest)
        try:
            execute('rsync', '--sparse', '--compress', '--dry-run', src, dest)
        except exception.ProcessExecutionError:
            execute('scp', src, dest)
        else:
            execute('rsync', '--sparse', '--compress', src, dest)
            

def vdisk_info(diskpath):
    "调用qemu-img获取disk文件的信息,主要用于和更新后的disk进行比较"
    result,_ = execute('qemu-img','info',diskpath)
    info = {}
    fetchvalue = lambda l : l.split(':')[-1].strip()
    for l in result.split('\n'):
        if "image:" in l:
            info['base'] = fetchvalue(l)
        if "file format:" in l:
            info['format'] = fetchvalue(l)
        if "virtual size:" in l:
            import re
            p = re.compile(r'.*\((.*) bytes\)')
            info['size'] = int(p.findall(l.split(':')[-1])[-1])
        if "disk size:" in l:
            info['realsize'] = fetchvalue(l)
    return info

_MKFS_COMMAND = {
   'default':'mkfs.ntfs --force --fast --label %(fs_label)s %(target)s',
   'linux':'mkfs.ext3 -L %(fs_label)s -F %(target)s',
   'windows':'mkfs.ntfs --force --fast --label %(fs_label)s %(target)s'
}

def mkfs(os_type, fs_label='tcloud'):
   mkfs_command = (_MKFS_COMMAND.get(os_type) or '' ) % locals()
   if mkfs_command:
       utils.execute(*mkfs_command.split(), run_as_root=True)


def vm_status(host,name):
    conn = get_connection(conf['life.migration.uri']% host)
    domain = conn.lookupByName(name)
    status,_ = domain.state(0)
    return power_state.name(status)


def vminfo(conn,instance,type):
    '''
    首先得到一个周期差：cpu_time_diff = cpuTimenow — cpuTime(t seconds ago)

    然后根据这个差值计算实际使用率：%CPU = 100 × cpu_time_diff / (t × nr_cores × 109)
    nr_cores是计算单核使用率，整体使用率则不需要
    @param instance:
    @return:
    '''

    if type == 'cpu':
        cpu_time1 = get_vm_info(conn,instance.name)['cpu_time']
        time.sleep(1)
        cpu_time2 = get_vm_info(conn,instance.name)['cpu_time']
        return 100*(cpu_time2-cpu_time1)/(1*(10**9))
    if type == 'disk':
        rbytes,wbytes = get_disk_stat(conn,instance.name)
        return rbytes,wbytes
    if type == 'network':
        rx_bytes,tx_bytes = get_network_stat(conn,instance.name)
        return rx_bytes,tx_bytes

def hostinfo(host,type,password):
    '''
    用于远程获取cpu，mem等主机信息
    @param host:
    @param type:
    @param password:
    @return:
    '''
    from cvirt.common import ssh
    conn = ssh.get_connection(host,username='root',password=password)
    cmd = "python -c 'import psutil;print %s'"
    if type == 'cpu':
        return float((conn.execute(cmd % "psutil.cpu_percent()")[-1][0]).strip())
    if type == 'mem':
        result = (conn.execute(cmd % "psutil.used_phymem(),psutil.used_virtmem()")[-1][0]).split()
        return map(int,result)
    if type == 'network':
        result = (conn.execute('python -c "import psutil;n= psutil.network_io_counters();'
                              'print n.bytes_recv,n.bytes_sent"')[-1][0]).split()
        return map(int,result)
    if type == 'mem_percent':
        return float((conn.execute(cmd % "psutil.phymem_usage().percent")[-1][0]).strip())


def get_vm_info(conn, instance_name):
    virt_dom = conn.lookupByName(instance_name)
    (state, max_mem, mem, num_cpu, cpu_time) = virt_dom.info()
    return {'state': state,
            'max_mem': max_mem,
            'mem': mem,
            'num_cpu': num_cpu,
            'cpu_time': cpu_time}


#Function to return a list of block devices used.
def get_target_devices(dom, dev):
    #Create a XML tree from the domain XML description.
    tree=ElementTree.fromstring(dom.XMLDesc(0))

    #The list of block device names.
    devices=[]

    #Iterate through all disk target elements of the domain.
    for target in tree.findall("devices/" + dev + "/target"):
        #Get the device name.
        dev=target.get("dev")

        #Check if we have already found the device name for this domain.
        if not dev in devices:
            devices.append(dev)

    #Completed device name list.
    return devices

def get_disk_stat(conn,name):
    dom = conn.lookupByName(name)
    #Initialize our block stat counters.
    rreq=0
    rbytes=0
    wreq=0
    wbytes=0

    #Iterate through each device name used by this domain.
    for dev in get_target_devices(dom, 'disk'):
        #Retrieve the block stats for this device used by this domain.
        try:
            stats=dom.blockStats(dev)
        except:
            stats = [0,0,0,0]

        #Update the block stat counters
        rreq+=stats[0]
        rbytes+=stats[1]
        wreq+=stats[2]
        wbytes+=stats[3]

    return rbytes,wbytes

def get_network_stat(conn,name):
    dom = conn.lookupByName(name)
    #Initialize our interface stat counters.
    rx_bytes=0
    rx_packets=0
    rx_errs=0
    rx_drop=0
    tx_bytes=0
    tx_packets=0
    tx_errs=0
    tx_drop=0

    #Iterate through each device name used by this domain.
    for dev in get_target_devices(dom, 'interface'):
        #Retrieve the interface stats for this device used by this domain.
        stats=dom.interfaceStats(dev)

        #Update the interface stat counters
        rx_bytes+=stats[0]
        rx_packets+=stats[1]
        rx_errs+=stats[2]
        rx_drop+=stats[3]
        tx_bytes+=stats[4]
        tx_packets+=stats[5]
        tx_errs+=stats[6]
        tx_drop+=stats[7]

    return rx_bytes,tx_bytes